import unittest
import sys, os
parent_dir = os.path.abspath("..")
sys.path.append(parent_dir)

from graph import Graph
from graph import GraphError


class TestGraph(unittest.TestCase):

    def setUp(self):
        self.G = Graph()

    def testAddVertex(self):
        G = self.G

        v1 = G.vertex(56)
        self.assertEqual(v1.id, 56)
        self.assertEqual(repr(v1), "56")
        self.assertEqual(v1.degree, 0)
        self.assertEqual(v1.order, 0)
        self.assertEqual(v1.neighbors, [])
        self.assertEqual(v1.graph, G)

        v2 = G.vertex('qwe')
        self.assertEqual(v1.neighbors, [])
        self.assertEqual(v2.id, 'qwe')
        self.assertEqual(repr(v2), "'qwe'")

        v3 = G.vertex('56')
        self.assertEqual(v1.degree, 0)
        self.assertEqual(v2.neighbors, [])
        self.assertEqual(v3.id, '56')

        self.assertEqual([v.id for v in G.V], [56, 'qwe', '56'])
        self.assertEqual([repr(v) for v in G.V], ["56", "'qwe'", "'56'"])
        self.assertEqual([v.degree for v in G.V], [0, 0, 0])
        self.assertEqual([v.neighbors for v in G.V], [[], [], []])
        self.assertEqual([v.order for v in G.V], [0, 1, 2])
        self.assertEqual(G.E, [])

    def testAddEdge(self):
        G = self.G

        v1 = G.vertex(1)
        v2 = G.vertex(2)
        v3 = G.vertex(3)

        e1 = G.edge(1, 2)
        self.assertEqual(v1.neighbors, [v2])
        self.assertEqual(v1.degree, 1)
        self.assertEqual(v2.neighbors, [v1])
        self.assertEqual(v2.degree, 1)
        self.assertEqual(v3.neighbors, [])
        self.assertEqual(v3.degree, 0)
        self.assertEqual(repr(e1), "(1, 2)")
        self.assertEqual(e1.id, (1, 2))
        self.assertEqual(e1.order, 0)
        self.assertEqual(e1.source, v1)
        self.assertEqual(e1.target, v2)
        self.assertEqual(e1.graph, G)

        e2 = G.edge(v3, v1)
        self.assertEqual(v1.neighbors, [v2, v3])
        self.assertEqual(v3.neighbors, [v1])
        self.assertEqual(e2.id, (3, 1))

        e3 = G.edge(3, v2)
        self.assertEqual(v2.neighbors, [v1, v3])
        self.assertEqual(v3.neighbors, [v1, v2])
        self.assertEqual(e3.id, (3, 2))
        self.assertEqual(e3.source, v3)
        self.assertEqual(e3.target, v2)

        self.assertEqual([v.id for v in G.V], [1, 2, 3])
        self.assertEqual([v.order for v in G.V], [0, 1, 2])
        self.assertEqual([e.id for e in G.E], [(1,2), (3,1), (3,2)])
        self.assertEqual([e.source for e in G.E], [v1, v3, v3])
        self.assertEqual([e.order for e in G.E], [0, 1, 2])

    def testDegree(self):
        G = self.G

        v1 = G.vertex(1)
        v2 = G.vertex(2)
        v3 = G.vertex(3)
        v4 = G.vertex(4)

        self.assertEqual(v1.degree, 0)

        self.assertEqual([v.id for v in G.V], [1, 2, 3,4])
        self.assertEqual(G.E, [])
        self.assertEqual(len(G), 4)

        G.edge(1, 2)
        G.edge(3, 2)
        G.edge(2, 4)

        self.assertEqual(v1.degree, 1)
        self.assertEqual(v2.degree, 3)
        self.assertEqual(v3.degree, 1)
        self.assertEqual(v4.degree, 1)

    def testOrder(self):
        G = self.G

        G.vertex('a')
        G.vertex('b')
        G.vertex('c')
        G.vertex('d')
        G.vertex('e')

        G.edge('c','a')
        # try create duplicate edge
        self.assertRaises(GraphError, G.edge, G.vorder(0), G.vorder(2))

        G.edge('b','e')
        self.assertEqual(G['e']['b'], G.eorder(1))
        self.assertEqual(G[('b','e')], G.eorder(1))

        # create edge ('a', 'd')
        G.edge(G.vorder(3), 'a')
        self.assertEqual(G[G.eorder(0)][G.eorder(2)], G['a'])
        self.assertEqual(G[G.eorder(2)][G.eorder(0)], G.vorder(0))

        # create edge ('a', 'e')
        G.edge(G.vorder(0), G.vorder(4))
        self.assertEqual(repr(G['a'].neighbors), "['c', 'd', 'e']")
        self.assertEqual(G[('e','a')][G.vorder(4)], G['a'])

        self.assertEqual(repr(G), "{'a': ['c', 'd', 'e'], 'b': ['e'], 'c': ['a'], 'd': ['a'], 'e': ['a', 'b']}")

        # try create loop ('a', 'a')
        self.assertRaises(GraphError, G.edge, G[G.eorder(3)][G.eorder(2)], G[('c','a')][G.vorder(2)])

        self.assertEqual([v.id for v in G.V], ['a', 'b', 'c', 'd', 'e'])
        self.assertEqual(G.E, [G[('a','c')], G['e']['b'], G.eorder(2), G[('a','e')]])

        for v in G.V:
            self.assertEqual(G.vorder(v.order), v)

        for e in G.E:
            self.assertEqual(G.eorder(e.order), e)

    def testContains(self):
        G = self.G

        self.assertEqual(1 in G, False)
        v1 = G.vertex(1)
        self.assertEqual(1 in G, True)
        self.assertEqual(2 in G, False)
        self.assertEqual(v1 in G, True)

        self.assertEqual('a' in G, False)
        v2 = G.vertex('a')
        self.assertEqual('a' in G, True)
        self.assertEqual('b' in G, False)
        self.assertEqual(v2 in G, True)

        e = G.edge(1, 'a')
        self.assertEqual(e in G, True)
        self.assertEqual((1, 'a') in G, True)
        self.assertEqual(('a', 1) in G, True)
        self.assertEqual((2, 'b') in G, False)
        self.assertEqual((v1, v2) in G, True)
        self.assertEqual((v1.id, v2) in G, True)
        self.assertEqual(G[1][v2] in G, True)

    def testDeleteVertex(self):
        G = self.G

        v1 = G.vertex(1)
        G.delete(v1)
        self.assertEqual(G.V, [])

        v1 = G.vertex('a')
        v2 = G.vertex('b')
        v3 = G.vertex('c')
        self.assertEqual(G.V, [v1, v2, v3])
        del G['a']
        self.assertEqual(G.V, [v2, v3])

        e = G.edge(v2, v3)
        G.delete(v2)
        self.assertEqual(G.V, [v3])

        v1 = G.vertex(1)
        v2 = G.vertex(2)
        v4 = G.vertex(4)
        self.assertEqual(G.V, [v3, v1, v2, v4])

        e1 = G.edge(v1,v2)
        e2 = G.edge(v3,v4)
        e3 = G.edge(v1,v3)
        del G[1]
        self.assertEqual(G.V, [v3, v2, v4])
        self.assertEqual(v2.neighbors, [])
        self.assertEqual(G[v3][v4], e2)
        self.assertEqual(G.E, [e2])
        del G[2]
        self.assertEqual(G.E, [e2])
        self.assertEqual(len(G), 2)
        del G[4]
        self.assertEqual(G.E, [])
        self.assertEqual(G.V, [v3])
        del G['c']
        self.assertEqual(G.E, [])
        self.assertEqual(G.V, [])

        self.assertRaises(GraphError, G.delete, 'c')


# TODO:
#1. Sdelat' tak chtoby edge na repr() vozvrashala tuple (s,t), esli _dir sovpadaet s _dir grafa,
# inache vozvrashat' (s,t,{dir:True/False}), izmenit' sootvetstvenno metody isedge(), konstruktor Edge i t.d.
# 1.5: ispravit Graph.E() chtoby daval pravil'ny rezul'tat
# s uchetom directed i undirected edgey.
# 2. Dobavit' vozmozhnost' ukazyvat' esli graf mozhet imet' multiedge, v zavisimosti vozvrashat'
# ili ne vozvrashat' oshibku v Edge.__init__()
# 3. Tak zhe s loop-free graphami
# 4. Izmenit' formirovanie stroki v Vertex.__repr__() i Graph.__repr__() pri pomoshi dict()
# 5. Napisat' na vse eto unittesty



# TODO:
# sdelat' ierarhiyu grafov, prostoy graf, directed dirgraph, mixedgraph(graph,digraph),
# multigraph(mixedgraph), pseudograph(multigraph), takim obrazom posledniy pseudograph budet
# obladat' vsemi vozmozhnostyami predydushih, chto dumayu budet redko ispol'zovatsya,
# klassy Vertex i Edge tozhe budut nasledovat'sya vmeste s grafami, takzhe kak i GraphError

if __name__ == "__main__":
    unittest.main()
